package com.xieyong.mywebsite.service.Impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xieyong.mywebsite.exception.MyCustomizeException;
import com.xieyong.mywebsite.mapper.UserMapper;
import com.xieyong.mywebsite.model.dto.Result;
import com.xieyong.mywebsite.model.entity.User;
import com.xieyong.mywebsite.service.FileService;
import com.xieyong.mywebsite.service.UserService;
import com.xieyong.mywebsite.util.Md5Util;
import com.xieyong.mywebsite.util.RedisUtil;
import com.xieyong.mywebsite.util.ThreadLocalUtil;
import com.xieyong.mywebsite.util.TokenUtil;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private FileService fileService;
    @Value("${app.default-avatar}")
    private String defaultAvatar;

    @Override
    public void register(String username, String password) {
        User u = userMapper.selectByUserName(username);
        if (u != null) {
            throw new MyCustomizeException("用户名被占用");
        } else {
//        加密密码
            String md5String = Md5Util.md5(password);
//        添加用户
            userMapper.register(username,md5String);
        }
    }

    @Override
    public String login(String username, String password) {
//        根据用户名查询用户
        User loginUser = userMapper.selectByUserName(username);
//        判断用户是否存在
        if (loginUser == null) {
            throw new MyCustomizeException("用户名或者密码错误");
        }
//          判断密码是否正确
        if(Md5Util.verify(password, loginUser.getPassword())) {
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", loginUser.getId());
            claims.put("username", loginUser.getUsername());
            String token = TokenUtil.createToken(claims, loginUser.getUsername());
//            存储令牌到redis
            RedisUtil.set(token,token,1, TimeUnit.DAYS);
            return token;
        } else {
            throw new MyCustomizeException("用户名或者密码错误");
        }
    }

    @Override
    public User findById(Integer id) {
        return userMapper.selectById(id);
    }

    @Override
    public User findByUserName(String username) {
        return userMapper.selectByUserName(username);
    }

    @Override
    public List<User> getAllByRole(String role) {
        return userMapper.selectAllByRole(role);
    }

    @Override
    public List<User> getAll() {
        return userMapper.selectAll();
    }

    @Override
    public List<User> getBatch(List<User> users,String type) {
        List<User> list = new ArrayList<>();
        for (User user : users) {
            User dbuser = userMapper.selectById(user.getId());
            if(dbuser != null && dbuser.getRole().equals(type)) {
                list.add(dbuser);
            }
        }
        return list;
    }


    @Override
    public PageInfo<User> getPage(Integer pageNum, Integer pageSize, User user) {
    //        开启分页查询
        PageHelper.startPage(pageNum,pageSize);
        List<User> users = userMapper.selectByCondition(user);
        return PageInfo.of(users);
    }

    @Override
    public void add(User user) {
//        根据新增的账号名称查询数据库， 是否存在同样账号的数据
        User dbuser = userMapper.selectByUserName(user.getUsername());
        if (dbuser != null){
            throw new MyCustomizeException("账号重复:" + user.getUsername());
        } else {
            this.directAdd(user);
        }
    }

    public void directAdd(User user) {
//        如果没有密码，设置默认密码
        if (!StringUtils.hasLength(user.getPassword())) {
            user.setPassword(Md5Util.md5("123456"));
        }
//        如果没有名称，设置默认名称
        if (!StringUtils.hasLength(user.getNickname())) {
            user.setNickname(user.getUsername());
        }
//        如果没有角色，设置默认角色
        if (!StringUtils.hasLength(user.getRole())) {
            user.setRole("USER");
        }
//        如果没有头像，设置默认头像
        if (!StringUtils.hasLength(user.getAvatar())) {
            user.setAvatar(defaultAvatar);
        }
        userMapper.insert(user);
    }

    @Override
    public void update(User user) {
//        更新前移除头像
        fileService.removeFile(userMapper.selectById(user.getId()).getAvatar());
        userMapper.updateById(user);
    }

    @Override
    public void updateAvatar(String avatarUrl) {
        Map<String, Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
//        更新前移除头像
        fileService.removeFile(userMapper.selectById(id).getAvatar());
        userMapper.updateAvatar(avatarUrl, id);
    }

    @Override
    public void updatePassword(Map<String, String> params, String token) {
//        校验参数
        String oldPassword = params.get("oldPassword");
        String newPassword = params.get("newPassword");
        String confirmPassword = params.get("confirmPassword");

        if(!StringUtils.hasLength(oldPassword) || !StringUtils.hasLength(newPassword) || !StringUtils.hasLength(confirmPassword)) {
            throw new MyCustomizeException("缺少必要参数");
        }

//        判断原密码是否正确
        Map<String, Object> map = ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User loginUser = userMapper.selectByUserName(username);
        System.out.println(loginUser.getPassword());
        System.out.printf(oldPassword);
        if(!Md5Util.verify(oldPassword, loginUser.getPassword())) {
            throw new MyCustomizeException("原密码错误");
        }

//        判断两次密码是否填写一致
        if(!newPassword.equals(confirmPassword)) {
            throw new MyCustomizeException("两次密码填写不一致!");
        }
//        调用mapper更新密码
        Integer id = (Integer) map.get("id");
        userMapper.updatePassword(Md5Util.md5(newPassword), id);
//        从redis中删除原来的token
        RedisUtil.del(token);
    }

    @Override
    public void deleteById(Integer deleteId) {
        Map<String, Object> map = ThreadLocalUtil.get();
        Integer operatorId = (Integer) map.get("id");
        userMapper.deleteById(deleteId,operatorId);
    }

    @Override
    public void deleteBatch(List<Integer> ids) {
        for (Integer id : ids) {
            deleteById(id);
        }
    }

    @Override
    public void exportBatch(HttpServletResponse response, List<User> userList, String type) {
//        1,通常认为前端已经将数据传递过来了，所以数据已经拿到
//        但是这里还是可以调用方法从数据库查出数据
        List<User> batch = this.getBatch(userList,type);
//        2,调用方法写数据
        try{
            exportData(response,batch);
        } catch (Exception e) {
            throw new MyCustomizeException("导出出错");
        }
    }

    @Override
    public void exportAll(HttpServletResponse response, String role){
//        1,获取全部数据
        List<User> userList = userMapper.selectAllByRole(role);
//        2,调用方法写数据
        try{
            exportData(response,userList);
        } catch (Exception e) {
            throw new MyCustomizeException("导出出错");
        }
    }

    public void exportData(HttpServletResponse response,List<User> userList) throws Exception {
//        2,构建Writter对象
        ExcelWriter writer = ExcelUtil.getWriter(true);
//        3,设置中文表头
        writer.addHeaderAlias("username","账号");
        writer.addHeaderAlias("nickname","名称");
        writer.addHeaderAlias("phone","电话");
        writer.addHeaderAlias("email","邮箱");
        writer.addHeaderAlias("role","角色");
//        默认的会将未添加alias的属性也写出，如果只想写出添加了别名的字段，可以调用该方法
        writer.setOnlyAlias(true);
//        4,写出数据到writer
        writer.write(userList);
//        5,设置输出的文件名称以及输出流的头信息
        String filename = URLEncoder.encode("用户信息", StandardCharsets.UTF_8);
//        固定的头信息
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + filename + ".xlsx");
//        6,写出到输出流，并关闭writer
        ServletOutputStream out = response.getOutputStream();
        writer.flush(out);
        writer.close();
    }

    @Override
    public Result<File> importData(MultipartFile file) throws Exception {
//        1,拿到输入流，构建reader
        InputStream inputStream = file.getInputStream();
        ExcelReader reader = ExcelUtil.getReader(inputStream);
//        2, 通过reader读取Excel里面的数据
        reader.addHeaderAlias("账号","username");
        reader.addHeaderAlias("名称","name");
        reader.addHeaderAlias("电话","phone");
        reader.addHeaderAlias("邮箱","email");
        reader.addHeaderAlias("角色","role");
        List<User> userList = reader.readAll(User.class);
//        3,将数据写入数据库中
        List<User> repeatUsers = new ArrayList<>();
        for (User user : userList) {
//            如果有重复数据，将重复数据的账号名保存到列表中，否则直接写入
            User dbuser = userMapper.selectByUserName(user.getUsername());
            if (dbuser != null){
                repeatUsers.add(dbuser);
            } else {
                this.directAdd(user);
            }
        }
        if(!repeatUsers.isEmpty()){
//            如果存在重复数据，则提示重复的账号
            StringBuffer sb = new StringBuffer();
            for (User repeatUser : repeatUsers) {
                sb.append(repeatUser.getUsername()).append(" , " );
            }
            sb.deleteCharAt(sb.length() - 2);
            throw new MyCustomizeException("以下账号重复未导入:  " + sb);
        } else {
            return Result.success();
        }
    }
}
